#!/usr/bin/env python
"""
##############################################################################
用法：./diffall.py dir1 dir2 [-v verbose(0/1)] [-b blocksize(int)]
递归地目录树比较：报告dir1和dir2各自的独有文件，报告dir1和dir2同名但内容不同的文件，dir1和
dir2下所有同名子目录及其目录进行相同操作。输出的末尾打印差异的总结。
##############################################################################
"""

import os
import sys
import dirdiff
import cpall

BLOCKSIZE = 1024 * 1024
VERBOSE = False


def intersect(seq1, seq2):
    """
    返回seq1和seq2的所有共有项；
    也可以用set(seq1) & set(seq2)，不过集合内的顺序是随机的，
    故而可能丢失任何依赖于平台的目录顺序。
    """
    return [item for item in seq1 if item in seq2]


def comparetrees(dir1, dir2, diffs, verbose=VERBOSE, blocksize=BLOCKSIZE):
    """
    比较两个目录树中所有子目录和文件；使用二进制来阻止Unicode解码和换行符转换，
    因为目录树可能含有二进制文件和文本文件；
    可能需要listdir的bytes参数来处理某些平台上不可解码的文件名。
    """
    # 比较文件名列表
    print('-' * 79)
    print('正在比较目录{}和{}下的文件列表'.format(dir1, dir2))
    fns1 = os.listdir(dir1)
    fns2 = os.listdir(dir2)

    uniques1 = dirdiff.diff(fns1, fns2)
    uniques2 = dirdiff.diff(fns2, fns1)

    dirdiff.reportdiffs(uniques1, uniques2, dir1, dir2)

    if uniques1:
        for unique in uniques1:
            diffs.append('{}的独有文件：{}'.format(dir1, unique))

    if uniques2:
        for unique in uniques2:
            diffs.append('{}的独有文件：{}'.format(dir2, unique))

    # 比较共有文件的内容
    print('正在比较目录{}和{}下的共有文件内容'.format(dir1, dir2))
    commons = intersect(fns1, fns2)
    missed = commons[:]

    for fn in commons:
        fp1 = os.path.join(dir1, fn)
        fp2 = os.path.join(dir2, fn)
        if os.path.isfile(fp1) and os.path.isfile(fp2):
            missed.remove(fn)
            f1 = open(fp1, 'rb')
            f2 = open(fp2, 'rb')
            while True:
                bytes1 = f1.read(blocksize)
                bytes2 = f2.read(blocksize)
                if (not bytes1) and (not bytes2):
                    if verbose:
                        print('{}内容相同'.format(fn))
                    break
                if bytes1 != bytes2:
                    diffs.append('{}和{}下的{}内容不同'.format(dir1, dir2, fn))
                    print('{}内容不同'.format(fn))
                    break

    # 递归以比较共有目录
    for fn in commons:
        fp1 = os.path.join(dir1, fn)
        fp2 = os.path.join(dir2, fn)
        if os.path.isdir(fp1) and os.path.isdir(fp2):
            missed.remove(fn)
            comparetrees(fp1, fp2, diffs, verbose)

    # 同名但是一个是文件、一个是目录？
    for fn in missed:
        diffs.append('{}和{}下的{}不同类型但同名'.format(dir1, dir2, fn))
        print('{}不同类型但同名'.format(fn))


def getargs(verbose=VERBOSE, blocksize=BLOCKSIZE):
    if len(sys.argv) < 3 and len(sys.argv) > 5:
        print('用法：./dirdiff.py dir1 dir2')
        sys.exit()
    else:
        dir1, dir2 = sys.argv[1:3]

        if '-v' in sys.argv:
            verbose = cpall.getInt('-v')
        if '-b' in sys.argv:
            blocksize = cpall.getInt('-b')

        if verbose:
            verbose = True
        else:
            verbose = False

        return dir1, dir2, verbose, blocksize


def command():
    dir1, dir2, verbose, blocksize = getargs()
    diffs = []
    comparetrees(dir1, dir2, diffs, verbose, blocksize)
    print('=' * 79)

    if not diffs:
        print('完全相同')
    else:
        print('不同之处：', len(diffs))
        for diff in diffs:
            print('-', diff)


if __name__ == '__main__':
    command()
